"""
pagamiago - free software script to send PayPal payments

Copyright (C) 2018 Alyssa Rosenzweig

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.
"""

import re
import sys
import urllib.parse
import subprocess

from lxml.html import parse
from io import StringIO

# reCAPTCHA support is an edge case; librecaptcha (to handle it) is accordingly
# an optional dependency. If it's missing, that's okay -- just throw a run-time
# error in the off-chance that they try to reCAPTCHA us anyway. 

LIBRECAPTCHA_MISSING = """
ERROR: PayPal tried to send us a reCAPTCHA, rather than a traditional
CAPTCHA, for reasons that are not entirely clear. Were you doing something
suspicious on the account, perhaps? To proceed, try installing librecaptcha
(`python3-librecaptcha` in Debian), which enables ReCAPTCHA support"""

try:
    from librecaptcha import get_token
except ImportError:
    def get_token(key, base):
        print(LIBRECAPTCHA_MISSING[1:])
        sys.exit(1)


def form_fill(form):
    p = {}

    for (k, v) in form.form_values():
        p[k] = v

    return p


def silent_captcha(text, tree, form):
    captcha = tree.cssselect(".captcha-image img")
    url = captcha[0].attrib["src"]

    # Important that shell=False to mitigate security issues !
    view = subprocess.Popen(["feh",
                             "-Bwhite",
                             url],
                            stdin=subprocess.PIPE,
                            stdout=subprocess.PIPE,
                            stderr=None,
                            close_fds=True,
                            shell=False)

    form.fields["captcha"] = input("\rType the letters on-screen: ")

    # CAPTCHA input over; close window
    view.kill()
    print("")

    return form_fill(form)


def recaptcha(text, tree, form):
    data = {
        "jse": re.search("jsenode.value = '([^']+)';", text).group(1)
    }

    for (k, v) in form.form_values():
        if k in ["_csrf", "_sessionID"]:
            data[k] = v

    # Fire up librecaptcha to actually resolve the CAPTCHA
    iframe_src = form.cssselect("iframe")[0].attrib["src"]
    api_key = urllib.parse.parse_qs(
        urllib.parse.urlparse(iframe_src).query)["siteKey"][0]

    data["recaptcha"] = get_token(api_key, "https://paypal.com")

    return data


CAPTCHAS = {
    "recaptcha": recaptcha,
    "silentcaptcha": silent_captcha
}


# If the page in `text` contains a CAPTCHA, handle it appropriately. If
# not, pass through the original text.
def handle_captcha(session, web_page_content):
    tree = parse(StringIO(web_page_content)).getroot()

    captcha_standalone = tree.cssselect("#captcha-standalone")

    if captcha_standalone is None or len(captcha_standalone) == 0:
        return web_page_content

    try:
        form = tree.forms[0]
    except IndexError:
        return web_page_content

    captcha_type = captcha_standalone[0].attrib["data-captcha-type"]

    try:
        data = CAPTCHAS[captcha_type](web_page_content, tree, form)
    except KeyError:
        return web_page_content

    return session.send_request(
        "/auth/validatecaptcha",
        data,
        mode="post_form",
        plain_headers=True)
